<template>
  <el-select
    ref="multipleSelectRef"
    class="yinta-multiple-select-vue3"
    :model-value="selected"
    multiple
    placeholder="Select"
    clearable
    :collapse-tags="collapseTags"
    @remove-tag="removeSingleTag"
    @visible-change="visibleChange"
    @clear="clearAllSelect"
  >
    <div class="yinta-multiple-select-custom-el-select">
      <div class="el-select-pop-search-container" ref="headerRef">
        <i class="el-icon-search icon-search"></i>
        <input type="text" v-model="keyword" placeholder="搜索内容" class="el-select-pop__input" />
      </div>
      <el-option v-if="multipleLimit === 0 && filterOptions.length > 0" label="全选" value="全选">
        <el-checkbox v-model="isSelectAll" :indeterminate="!isSelectAll && tempSelected.length !== 0" @click.prevent.native="toggleSelectAll"> 全选 </el-checkbox>
      </el-option>
      <div>
        <el-option v-for="(item, index) in limitOptions" :key="item[attrs.value]" :label="item[attrs.label]" :value="item[attrs.value]">
          <el-checkbox
            v-model="item.isCheck"
            @click.prevent.native.shift="shiftSelectSingle(item[attrs.value], index)"
            @click.prevent.native.exact="toggleSelectSingle(item[attrs.value], index)"
            :disabled="multipleLimit !== 0 && tempSelected.length >= multipleLimit && !item.isCheck"
          >
            <span :title="item[attrs.label]">{{ item[attrs.label] }}</span>
          </el-checkbox>
          <el-button link type="primary" class="single-btn" @click="onlySelectSingle(item[attrs.value])">仅筛选此项</el-button>
        </el-option>
      </div>
      <div class="custom-multi-footer" ref="footerRef">
        <span class="shift-tips">提示：按住Shift可以快速多选</span>
        <div>
          <el-button @click="handleCancel">取消</el-button>
          <el-button type="primary" @click="handleSave">确认</el-button>
        </div>
      </div>
    </div>
  </el-select>
</template>

<script setup>
import { ref, watch, computed, onMounted, toRef } from 'vue'

const props = defineProps({
  options: {
    type: Array,
    default: () => []
  },
  modelValue: {
    type: Array,
    default: () => []
  },
  placeholder: {
    type: String,
    default: ''
  },
  remote: {
    type: Boolean,
    default: false
  },
  collapseTags: {
    type: Boolean,
    default: false
  },
  attrs: {
    type: Object,
    default: () => {
      return {
        label: 'label',
        value: 'value'
      }
    }
  },
  showLimit: {
    type: Number,
    default: 300
  },
  multipleLimit: {
    type: Number,
    default: 0
  }
})

const multipleSelectRef = ref()
const headerRef = ref()
const footerRef = ref()
const keyword = ref('')
const isSelectAll = ref(false)
const selected = ref([])
const tempSelected = ref([])
const tempOptions = ref([])

const emit = defineEmits(['search-change', 'update:modelValue'])

function findAncestorWithClass(element, className) {
  // 从当前元素开始向上遍历父元素
  while (element && !element.classList.contains(className)) {
    element = element.parentNode
  }
  // 返回找到的父元素，如果没有找到则返回null
  return element
}

const initData = () => {
  keyword.value = ''
  tempSelected.value = [...props.modelValue]
  selected.value = [...props.modelValue]
}

const updateSelectAll = () => {
  if (filterOptions.value.length !== 0) {
    if (keyword.value === '') {
      isSelectAll.value = filterOptions.value.length === tempSelected.value.length
    } else {
      if (filterOptions.value.some((item) => !tempSelected.value.includes(item[props.attrs.value]))) {
        isSelectAll.value = false
      } else {
        isSelectAll.value = true
      }
    }
  } else {
    isSelectAll.value = false
  }
}

const filterOptions = computed(() => {
  return keyword.value === '' || keyword.value === undefined || props.remote ? tempOptions.value : tempOptions.value.filter((item) => item[props.attrs.label].indexOf(keyword.value) > -1)
})

const limitOptions = computed(() => {
  return filterOptions.value.slice(0, props.showLimit)
})

watch(
  () => props.options,
  (val) => {
    tempOptions.value = [...val]
  },
  {
    immediate: true
  }
)

watch(
  () => props.modelValue,
  () => {
    initData()
  },
  {
    immediate: true,
    deep: true
  }
)

watch(
  tempSelected,
  (arr) => {
    tempOptions.value.forEach((item) => {
      if (arr.includes(item[props.attrs.value])) {
        item.isCheck = true
      } else {
        item.isCheck = false
      }
    })
    updateSelectAll()
  },
  {
    deep: true,
    immediate: true
  }
)

watch(keyword, (val) => {
  emit('search-change', val)
  setTimeout(() => {
    updateSelectAll()
  }, 0)
})

onMounted(() => {
  const dropdown = findAncestorWithClass(headerRef.value, 'el-select-dropdown')
  const list = findAncestorWithClass(headerRef.value, 'el-scrollbar')
  dropdown.insertBefore(headerRef.value, list)
  dropdown.appendChild(footerRef.value)
  setTimeout(() => {
    dropdown.setAttribute('style', 'min-width: 350px !important')
  }, 0)
})

const visibleChange = (val) => {
  if (!val) {
    initData()
  }
}
const getLastIndex = () => {
  // 寻找正确的 index，方便 shift 多选
  let lastIndex = -1
  if (tempSelected.value.length > 0) {
    const lastVal = tempSelected.value[tempSelected.value.length - 1]
    lastIndex = filterOptions.value.findIndex((item) => item[props.attrs.value] === lastVal)
  }
  return lastIndex
}
const shiftSelectSingle = (val, index) => {
  const lastIndex = getLastIndex()
  if (lastIndex === -1 || tempSelected.value.includes(val)) {
    toggleSelectSingle(val, index)
  } else {
    if (props.multipleLimit > 0 && tempSelected.value.length >= props.multipleLimit) {
      return
    }
    let area = []
    if (lastIndex > index) {
      area = filterOptions.value.slice(index, lastIndex + 1).map((item) => item[props.attrs.value])
    } else {
      area = filterOptions.value.slice(lastIndex + 1, index + 1).map((item) => item[props.attrs.value])
    }
    if (props.multipleLimit > 0) {
      area = area.slice(0, props.multipleLimit - tempSelected.value.length)
    }
    tempSelected.value = [...new Set([...tempSelected.value, ...area])]
  }
}
const toggleSelectSingle = (val) => {
  const index = tempSelected.value.indexOf(val)
  if (index === -1) {
    if (props.multipleLimit > 0 && tempSelected.value.length >= props.multipleLimit) {
      return
    }
    tempSelected.value.push(val)
  } else {
    tempSelected.value.splice(index, 1)
  }
}
const toggleSelectAll = () => {
  const curSet = new Set(tempSelected.value)
  if (isSelectAll.value) {
    filterOptions.value.forEach((item) => {
      curSet.delete(item[props.attrs.value])
    })
  } else {
    filterOptions.value.forEach((item) => {
      curSet.add(item[props.attrs.value])
    })
  }
  tempSelected.value = [...curSet]
}

const removeSingleTag = (val) => {
  const tempIndex = tempSelected.value.indexOf(val)
  tempSelected.value.splice(tempIndex, 1)
  const index = selected.value.indexOf(val)
  selected.value.splice(index, 1)
  multipleSelectRef.value.visible = false
  emit('update:modelValue', selected.value)
}
const clearAllSelect = () => {
  tempSelected.value = []
  selected.value = []
  emit('update:modelValue', selected.value)
}
const onlySelectSingle = (val) => {
  selected.value = [val]
  multipleSelectRef.value.visible = false
  emit('update:modelValue', selected.value)
}
const handleCancel = () => {
  multipleSelectRef.value.visible = false
}
const handleSave = () => {
  selected.value = [...tempSelected.value]
  multipleSelectRef.value.visible = false
  emit('update:modelValue', selected.value)
}
</script>

<style lang="scss">
.yinta-multiple-select-vue3 {
  .el-select__tags-text {
    max-width: 100px;
  }
}

.yinta-multiple-select-custom-el-select {
  .el-select-dropdown__item {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding-right: 20px !important;
    .single-btn {
      display: none;
    }
    &:hover {
      .single-btn {
        display: block;
      }
    }
    &::after {
      display: none !important;
    }
  }
  .el-checkbox {
    width: calc(100% - 90px);
    display: flex;
  }
  .el-checkbox__label {
    display: inline-block;
    padding-left: 10px;
    line-height: 1;
    font-size: 12px;
    flex: 1;
    display: inline-block;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    margin-top: 1px;
  }

  .el-checkbox__input.is-checked .el-checkbox__inner {
    background-color: transparent;
    border-color: #1890ff;
  }
  .el-checkbox__input.is-checked .el-checkbox__inner::after {
    border-color: #1890ff;
  }
  .el-checkbox__input.is-indeterminate .el-checkbox__inner {
    background-color: #fff;
  }
  .el-checkbox__input.is-indeterminate .el-checkbox__inner::before {
    background-color: #1890ff;
  }
  .el-input__inner {
    min-height: 28px;
    height: unset;
  }
}
.el-select-pop-search-container {
  padding: 6px 16px;
  display: flex;
  align-items: center;
  .icon-search {
    position: absolute;
    top: 14px;
    left: 18px;
    color: #a6abb4;
    width: 14px;
    line-height: initial;
  }
  .el-select-pop__input {
    padding-left: 26px;
    border-radius: 0;
    border-style: none none solid;
    border-bottom: 1px solid #d9dbde;
    height: 32px;
    line-height: 32px;
    width: 100%;
    box-sizing: border-box;
    outline: none;
    font-size: 12px;
    &:focus {
      border-bottom-color: #1890ff;
    }
  }
}
.custom-multi-footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 46px;
  padding-right: 8px;
  border-top: 1px solid #e6e8eb;
  font-size: 12px;
  .shift-tips {
    margin-left: 8px;
    color: #a6abb4;
  }
}
</style>
